/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package coalescent.is.cef.command.listener;

import cef.command.CommandI;
import cef.command.listener.CommandOutputEvent;
import cef.command.listener.CommandOutputListener;
import cef.command.listener.CommandOutputListenerException;
import cef.command.listener.FigureEditorOutputComponent;

import coalescent.is.cef.command.AppNoteIS_Cmd;
import coalescent.is.cef.command.AppNoteIS_CmdState;

import commons.util.GUIUtil;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.DefaultCategoryItemRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.xy.XYSeries;

import static coalescent.is.cef.command.AppNoteIS_CmdState.Output;

import java.awt.*;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;

/**
 * @author Susanta Tewari
 * @version version
 * @history Created on 12/8/13.
 * @since since
 */
public class AppNoteIS_OC extends FigureEditorOutputComponent {

    private static final Stroke stroke = new BasicStroke(2.5f);

    @Override
    public void initComponents(CommandOutputEvent event) throws CommandOutputListenerException {

        final AppNoteIS_Cmd command = (AppNoteIS_Cmd) event.getSource();

        title = "Running-Time analysis using Simulated Data for a combination of Models";

        final XYSeries xyseries        = new XYSeries(title);
        final AppNoteIS_CmdState state = command.getState();

        figure = createGridPanel(state);
    }

    public static JPanel createGridPanel(AppNoteIS_CmdState state) {

        double[] thetas  = state.getThetas();
        int[] nVals      = state.getNVals();
        JPanel gridPanel = new JPanel(new GridLayout(thetas.length, nVals.length));

        for (int i = 0; i < thetas.length; i++) {

            for (int j = 0; j < nVals.length; j++) {

                if (!state.containsOutputBySize(i, j, 0)) {

                    gridPanel.add(new JLabel("No output"));

                    continue;
                }

                JPanel cellPanel = new JPanel(new GridLayout(1, 2));


                // data-1: sizeBased; data-2: timeBased
                DefaultCategoryDataset dataForSize = new DefaultCategoryDataset();
                DefaultCategoryDataset dataForTime = new DefaultCategoryDataset();
                final int ssIndex                  = state.getScaledSamplerIndex();
                final String measureKey            = state.getMeasureKey();
                final String[] samplers            = state.getSamplers();

                for (int k = 0; k < state.getCellSampleSize(); k++) {

                    String seriesKey            = k + "";
                    Output oS                   = state.getOutputBySize(i, j, k);
                    Output oT                   = state.getOutputByTime(i, j, k);
                    final double[] scaledBySize = scaleMeasure(oS, measureKey, ssIndex);
                    final double[] scaledByTime = scaleMeasure(oT, measureKey, ssIndex);

                    for (int m = 0; m < samplers.length; m++) {

                        dataForSize.addValue(scaledBySize[m], seriesKey, samplers[m]);
                        dataForTime.addValue(scaledByTime[m], seriesKey, samplers[m]);
                    }
                }

                cellPanel.add(createCellPanel(dataForSize, Color.white));
                cellPanel.add(createCellPanel(dataForTime, new Color(245, 245, 245)));    // very light grey


                // border
                final Border lineBorder = BorderFactory.createLineBorder(new Color(139, 5, 5), 2);
                final String borderTitle = "Theta: " + thetas[i] + " N: " + nVals[j];
                final TitledBorder titledBorder = BorderFactory.createTitledBorder(lineBorder,
                                                      borderTitle);

                titledBorder.setTitleJustification(TitledBorder.CENTER);
                cellPanel.setBorder(titledBorder);


                // add to grid
                gridPanel.add(cellPanel);
            }
        }

        return gridPanel;
    }

    private static double[] scaleMeasure(Output oT, String measureKey, int ssIndex) {

        final double[] result = oT.getMeasure(measureKey);
        double divisor        = result[ssIndex];

        for (int i = 0; i < result.length; i++) {
            result[i] /= divisor;
        }

        return result;
    }

    private static ChartPanel createCellPanel(CategoryDataset data_set, Color plotBgColor) {


        // axis
        final CategoryAxis x_axis = new CategoryAxis(null);
        final NumberAxis y_axis   = new NumberAxis(null);

        y_axis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());


        // renderer
        final DefaultCategoryItemRenderer renderer = new DefaultCategoryItemRenderer();

        renderer.setSeriesStroke(0, stroke);    // strokes
        renderer.setSeriesStroke(1, stroke);
        renderer.setSeriesStroke(2, stroke);
        renderer.setSeriesPaint(0, new Color(255, 182, 193));    // light pink for EGT
        renderer.setSeriesPaint(1, new Color(134, 230, 134));    // light green for SD
        renderer.setSeriesPaint(2, new Color(171, 216, 230));    // light blue for HUW


        // plot & chart
        final CategoryPlot plot = new CategoryPlot(data_set, x_axis, y_axis, renderer);

        plot.setDomainGridlinesVisible(false);
        plot.setRangeGridlinesVisible(false);
        plot.setBackgroundPaint(plotBgColor);

        final JFreeChart chart = new JFreeChart(null, null, plot, false);

        chart.setBackgroundPaint(Color.white);

        return new ChartPanel(chart);
    }

    public static void main(String[] args) throws Exception {

        String stateFulPath = "console/AppNoteIS_Stateful.xml";

        drawFromPath(stateFulPath);
    }

    public static void demo() {

        double[] thetas                = { 1.0, 3.0, 5.0 };
        int[] nVals                    = { 50, 75, 100 };
        final String[] samplers        = { "EGT", "SD", "HUW" };
        final AppNoteIS_CmdState state = new AppNoteIS_CmdState(thetas, nVals, samplers, 1);

        for (int i = 0; i < thetas.length; i++) {

            for (int j = 0; j < nVals.length; j++) {

                final double[][] m1_size = {
                    { 0.5, 1.0, 2.0 }, { (i + j + 1) * 0.5, 1.0, 3.0 }, { 2.0, 1.0, 2.5 }
                };
                final double[][] m1_time = {
                    { 0.9, 1.0, 2.0 }, { (i + j + 1) * 2.5, 1.0, 3.0 }, { 3.0, 1.0, 2.5 }
                };
                final double[] e         = new double[0];
                final int[] i_aRay       = new int[0];

                for (int k = 0; k < 3; k++) {

                    final Output oS = new Output(e, e, m1_size[k], i_aRay, i_aRay);
                    final Output oT = new Output(e, e, m1_time[k], i_aRay, i_aRay);

                    state.addOutputBySize(oS, i, j, k);
                    state.addOutputBySize(oT, i, j, k);
                }
            }
        }

        final JPanel gridPanel = createGridPanel(state);

        GUIUtil.displayGUI("", gridPanel);
    }

    public static void drawFromPath(String stateFulPath) throws Exception {

        final AppNoteIS_CmdState state = AppNoteIS_CmdState.load(stateFulPath);
        final JPanel gridPanel         = createGridPanel(state);

        GUIUtil.displayGUI("", gridPanel);
    }

    @Override
    public CommandOutputListener newInstance() {
        return new AppNoteIS_OC();
    }

    @Override
    public Class<? extends CommandI> getTargetCommandClass() {
        return AppNoteIS_Cmd.class;
    }
}
